home *** CD-ROM | disk | FTP | other *** search
/ NOVA - For the NeXT Workstation / NOVA - For the NeXT Workstation.iso / SourceCode / BarChart / ChartOfMatrix.m < prev    next >
Encoding:
Text File  |  1992-12-19  |  17.3 KB  |  643 lines

  1. /* 
  2.  * BarChart, A simple multi source loadable class.
  3.  *
  4.  *    Written by: Joe Freeman 7/92
  5.  *    
  6.  *    HSB color sweep stolen from some of Randy Nelson's code
  7.  *        should use NXEqualColor() for duplicates
  8.  *
  9.  */
  10.  
  11.  
  12. #import "ChartOfMatrix.h"
  13. #import <dpsclient/psops.h>
  14. #import <c.h>
  15. #import <stdio.h>
  16.  
  17. #define NUM_BOGUS    8    /* number of bars to draw when no data */
  18. #define COM_VERSION    3.3    /* version 3 ported to 3.0 */
  19.  
  20. @implementation ChartOfMatrix
  21.  
  22.  
  23. /*============================================================
  24.  *factory
  25.  *============================================================*/
  26.  
  27.  
  28. + initialize
  29. {
  30.     [super initialize];
  31.     [ChartOfMatrix setVersion:2];
  32.     return self;
  33. }
  34.  
  35. - (const char *)getInspectorClassName 
  36.     return "ChartOfMatrixInspector"; }
  37.  
  38. - initFrame:(NXRect *)r
  39. {
  40.     self = [super initFrame:r];
  41.     minSheetSet = 0.0;
  42.     maxSheetSet = 1.0;
  43.     COM_Flags.autoScale = YES;
  44.     COM_Flags.drawType = DRAW_V_BAR;
  45.     backgroundColor = NX_COLORWHITE;
  46.     highlightColor = NX_COLORBLACK;
  47.     highlightIndex = MAXINT;        /* don't show on default */
  48.     COM_Flags.drawFrame = YES;
  49.     hMargin = vMargin = 15.0;
  50.     hMargin = vMargin = 5.0;
  51.     numPrototypes = 5;
  52.     COM_Flags.randomBarColors = YES;
  53.     return self;
  54. }
  55.  
  56. - awake
  57. {
  58.     [super awake];
  59.     highlightIndex = MAXINT;
  60.     [self registerForDraggedTypes:&NXColorPboardType count:1];
  61.     return self;
  62. }
  63.  
  64. /*============================================================
  65.  * color dragging support
  66.  *============================================================*/
  67.  
  68. - (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
  69. {
  70.     if ([sender draggingSourceOperationMask] & NX_DragOperationGeneric) {
  71.     return NX_DragOperationGeneric;
  72.     } else {
  73.     return NX_DragOperationNone;
  74.         }
  75.     }
  76.  
  77. - (BOOL)performDragOperation:(id <NXDraggingInfo>)sender
  78. {
  79.     NXPoint p = [sender draggingLocation];
  80.     NXColor c = NXReadColorFromPasteboard([sender draggingPasteboard]);
  81.     [self setBackgroundColor: c];
  82.     return YES;
  83. }
  84. /*============================================================
  85.  * instance set / query methods
  86.  *============================================================*/
  87.  
  88. - setDataSrc:anObject
  89. {
  90.     dataSrc = anObject;
  91.     return self;
  92. }
  93.  
  94. - setGraphType:(int)drawCode
  95.     { COM_Flags.drawType = drawCode;  [self update]; return self; }
  96. - (int)graphType
  97.     { return COM_Flags.drawType; }
  98.  
  99. - takeRandomColorStateFrom:sender
  100.     { [self setRandomBarColorEnabled:[sender state]]; return self; }
  101. - setRandomBarColorEnabled:(BOOL)flag
  102.     { COM_Flags.randomBarColors = flag; [self update]; return self; }
  103. - (BOOL)isRandomBarColorEnabled
  104.     { return COM_Flags.randomBarColors; }
  105.     
  106. - takeBackgroundColorFrom:sender
  107.     { [self setBackgroundColor:  [sender color]]; return self; }
  108. - setBackgroundColor:(NXColor)aColor
  109.     {  backgroundColor = aColor; [self update]; return self; }
  110. - (NXColor)backgroundColor
  111.     { return backgroundColor; }
  112.     
  113. - takeHighlightColorFrom:sender
  114.     { [self setHighlightColor:  [sender color]]; return self; }
  115. - setHighlightColor:(NXColor)aColor
  116.     {  highlightColor = aColor; [self update]; return self; }
  117. - (NXColor)highlightColor
  118.     { return highlightColor; }
  119.  
  120.  
  121. /* set and query the size for the margins (in points) */
  122. - takeHMarginFrom:sender
  123.     { hMargin = [sender floatValue]; [self update]; return self; }
  124. - takeVMarginFrom:sender
  125.     { vMargin = [sender floatValue]; [self update]; return self; }
  126. - (float)hMargin
  127.     { return hMargin; }
  128. - (float)vMargin
  129.     { return vMargin; }
  130.  
  131. - takeAutoScaleStateFrom:sender
  132.     { COM_Flags.autoScale = [sender floatValue]; return self; }
  133. - setAutoScale:(BOOL)flag
  134.     { COM_Flags.autoScale = flag; [self update]; return self; }
  135. - (BOOL)autoScale
  136.     { return COM_Flags.autoScale; }
  137.  
  138. /* fancy controls */
  139. - takeFrameStateFrom:sender
  140.     { COM_Flags.drawFrame = [sender state]; [self update]; return self; }
  141. - (BOOL)frameState
  142.     { return COM_Flags.drawFrame; }
  143.  
  144. - takeNumProtosFrom:sender
  145.     { numPrototypes= [sender intValue]; [self update]; return self; }
  146. - (int)numProtos
  147.     { return numPrototypes; }
  148.  
  149. - takeBorderTypeFrom:sender
  150.     { borderType = [sender tag]; [self update]; return self; }
  151. - (int)borderType
  152.     { return borderType; }
  153.  
  154. /* when autoscale is off, set the min and max for the sheet */
  155. - takeMinValueFrom:sender
  156.     { minSheetSet = [sender floatValue]; return self; }
  157. - takeMaxValueFrom:sender
  158.     { maxSheetSet = [sender floatValue]; return self; }
  159. - (double)minValue
  160.     { return minSheetSet; }
  161. - (double)maxValue
  162.     { return maxSheetSet; }
  163.  
  164.   
  165. /*============================================================
  166.  * target/action
  167.  *============================================================*/
  168.  
  169. - (BOOL)acceptsFirstResponder { return YES; }
  170.  
  171. - copy:sender
  172. {
  173.     id pb = [Pasteboard new];    /* global pasteboard object */
  174.     NXStream    *st;        /* stream to collect data in */
  175.     char        *data;        /* actual data buffer */
  176.     int        length;        /* length of data */
  177.     int        maxLength;    /* (not used here) */
  178.     
  179.     /* declare that we will supply  a single type of data, eps */
  180.     [pb declareTypes:&NXPostScriptPboard num:1 owner:self];
  181.     
  182.     /* get a stream which writes to memory */
  183.     st = NXOpenMemory (NULL, 0, NX_WRITEONLY);
  184.     
  185.     /* find bounding box and then write it to the stream */
  186.     [self copyPSCodeInside:&bounds to:st];
  187.     
  188.     /* get actuall data buffer form stream */
  189.     NXGetMemoryBuffer (st, &data, &length, &maxLength);
  190.     
  191.     /* write data to pasteboard */
  192.     [pb writeType:NXPostScriptPboard data:data length:length ];
  193.     
  194.     /* dealocate stream including it's buffer */
  195.     NXCloseMemory (st, NX_FREEBUFFER );
  196.     
  197.  
  198.     return self;
  199. }
  200.  
  201. /*============================================================
  202.  * do real work
  203.  *============================================================*/
  204.  
  205. - (int)numLocations
  206. {
  207.     int numRows,numCols;
  208.  
  209.     if ([dataSrc respondsTo:@selector(getValue:forProperty:at:)]){
  210.         return [dataSrc count];
  211.     } else if ([dataSrc respondsTo:@selector(selectedCell)]){
  212.         [dataSrc getNumRows:&numRows numCols:&numCols];
  213.     return MAX(numRows,numCols);
  214.     } else if (!dataSrc) {
  215.         /* put up some dummy graph on palette */
  216.     return NUM_BOGUS;
  217.     }
  218.     return 0;
  219. }
  220.  
  221. - (float)valueOfLocation:(int)n
  222. {
  223.     static     float    data[NUM_BOGUS] = {.1, .3, .2, .7, .4, .8, .5, .98};
  224.     int numRows,numCols;
  225.     float theValue;
  226.     id      mrValue = [[DBValue alloc] init];
  227.  
  228.     if ([dataSrc respondsTo:@selector(getValue:forProperty:at:)]){
  229.        [dataSrc getValue:mrValue forProperty:mrExpression at:n];
  230.     theValue = [mrValue floatValue];
  231.     [mrValue free];
  232.     return theValue;
  233.     } else if ([dataSrc respondsTo:@selector(selectedCell)]){
  234.         [dataSrc getNumRows:&numRows numCols:&numCols];
  235.     if (numCols == 1) {
  236.         return [[dataSrc cellAt:n :0] floatValue];
  237.     } else {     /* numRows == 1 */
  238.         return [[dataSrc cellAt:0 :n] floatValue];
  239.     }
  240.     } else if (!dataSrc) {
  241.         return data[n];
  242.     }
  243.     
  244.     return 0.0;
  245. }
  246.  
  247. /*============================================================
  248.  * target action loading
  249.  *============================================================*/
  250.  
  251. - plotFromMatrix:sender
  252. {
  253.     if (!dataSrc && [sender respondsTo:@selector(selectedCell)]){
  254.         [self setDataSrc:sender];
  255.     } 
  256.     [self update];
  257.     return self;
  258. }
  259.  
  260. /*============================================================
  261.  * dbKit support
  262.  *============================================================*/
  263.  
  264. - associationContentsDidChange:association
  265. {
  266.     mrFetchGroup =     [association fetchGroup];
  267.     dataSrc =         [mrFetchGroup recordList];
  268.     highlightIndex =     [mrFetchGroup currentRecord];
  269.     mrExpression =     [association expression];
  270.     [self update];
  271.     return self;
  272. }
  273.  
  274. /*
  275. ** adh 7/27/92
  276. ** Do this so we redraw when values are updated in the UI
  277. */
  278. - association:association setValue:(DBValue *)value
  279. {
  280.     return [self update];
  281. }
  282.  
  283. - associationSelectionDidChange:association
  284. {
  285.     /* assume fetchgroup doesn't change so don't update mrFetchGroup */
  286.     highlightIndex =  [[association fetchGroup] currentRecord];
  287.     return [self update];
  288. }
  289.  
  290. - associationCurrentRecordDidDelete:association
  291. {
  292.     /* assumes record list is the same */
  293.     return [self update];
  294.     return self;
  295. }
  296.  
  297. /*============================================================
  298.  * do a selection with the mouse
  299.  *============================================================*/
  300.  
  301. - mouseDown:(NXEvent *)theEvent
  302. {
  303.     NXEvent     lastEvent;
  304.     NXRect     rectOfBar;
  305.     NXRect    drawRect;
  306.     int     longSize = [self numLocations];
  307.     int        index;
  308.  
  309.     
  310.     if ((COM_Flags.drawType != DRAW_H_BAR && 
  311.                 COM_Flags.drawType != DRAW_V_BAR ) ||
  312.         ![dataSrc respondsTo:@selector(getValue:forProperty:at:)]) {
  313.         NXBeep();
  314.     return nil;
  315.     }
  316.     
  317.     lastEvent = *theEvent;
  318.     [self convertPoint: &lastEvent.location fromView:nil];
  319.     /* modify the mouse position to be in psuedo drawing area coords */
  320.     lastEvent.location.x -= hMargin;
  321.     lastEvent.location.y -= vMargin;
  322.  
  323.     /* allow for the margins when calc'ing the bar moused down on */
  324.     drawRect = bounds;
  325.     drawRect.origin.x +=     hMargin;
  326.     drawRect.origin.y +=     vMargin;
  327.     drawRect.size.width -=     (2*hMargin);
  328.     drawRect.size.height -=     (2*vMargin);
  329.     
  330.     for ( index = 0 ; index < longSize; index++){
  331.         if (COM_Flags.drawType == DRAW_V_BAR) {
  332.         [self calcRect:&rectOfBar ofBar:index
  333.             insideRect:&drawRect
  334.             usingMin:NX_Y(&bounds) ];
  335.         rectOfBar.origin.y = bounds.origin.y;
  336.         rectOfBar.size.height = bounds.size.height;
  337.     } else  if (COM_Flags.drawType == DRAW_H_BAR) {
  338.         [self calcRect:&rectOfBar ofBar:index
  339.             insideRect:&drawRect
  340.             usingMin:NX_X(&bounds) ];
  341.         rectOfBar.origin.x = bounds.origin.x;
  342.         rectOfBar.size.width = bounds.size.width;
  343.     } else {
  344.         return nil;
  345.     }
  346.     if ([self mouse:&lastEvent.location inRect:&rectOfBar]){
  347.         [mrFetchGroup setCurrentRecord:index];
  348.         break;
  349.     }
  350.     }
  351.     return self;
  352.  
  353. /*============================================================
  354.  * target/action
  355.  *============================================================*/
  356.  
  357. - read:(NXTypedStream *)stream
  358. {
  359.         int tmpScale, tmpDrawFrame, tmpDrawType;
  360.     [super read:stream];
  361.     dataSrc =  NXReadObject(stream);
  362.     minField = NXReadObject(stream);
  363.     maxField =  NXReadObject(stream);
  364.     meanField = NXReadObject(stream);
  365.     /* the first demo palette went out as version 1 */
  366.     if (NXTypedStreamClassVersion(stream, "ChartOfMatrix")<2) {
  367.         NXReadTypes(stream,"ffffiii",
  368.                     &minSheetSet,&maxSheetSet,
  369.                     &vMargin,&hMargin,
  370.                     &tmpScale,
  371.                     &tmpDrawType,
  372.                     &tmpDrawFrame);
  373.         COM_Flags.autoScale = tmpScale;
  374.         COM_Flags.drawType = tmpDrawType;
  375.         COM_Flags.drawFrame = tmpDrawFrame;
  376.         COM_Flags.randomBarColors = YES;
  377.     } else {
  378.         NXReadTypes(stream,"ffffi",
  379.                     &minSheetSet,&maxSheetSet,
  380.                     &vMargin,&hMargin,
  381.                     &COM_Flags);
  382.     }
  383.     
  384.     backgroundColor = NXReadColor(stream);
  385.     highlightColor = NXReadColor(stream);
  386.     NXReadTypes(stream,"ii",
  387.                     &numPrototypes,
  388.                     &borderType);
  389.     return self;
  390. }
  391.  
  392. - write:(NXTypedStream *)stream
  393. {
  394.     [super write:stream];
  395.     NXWriteObjectReference(stream, dataSrc);
  396.     NXWriteObjectReference(stream, minField);
  397.     NXWriteObjectReference(stream, maxField);
  398.     NXWriteObjectReference(stream, meanField);
  399.     NXWriteTypes(stream,"ffffi",
  400.                     &minSheetSet,&maxSheetSet,
  401.                     &vMargin,&hMargin,
  402.                     &COM_Flags);
  403.     NXWriteColor(stream,        backgroundColor);
  404.     NXWriteColor(stream,        highlightColor);
  405.     NXWriteTypes(stream,"ii",
  406.                     &numPrototypes,
  407.                     &borderType);
  408.     return self;
  409. }
  410.  
  411. /*============================================================
  412.  *display
  413.  *============================================================*/
  414.  
  415. - calcMin:(float *)rMin andMax:(float *)rMax andMean:(float *)rMean;
  416. {
  417.     int    index;
  418.     int    longSize;
  419.     float    thisVal;
  420.     float    sumAll = 0;
  421.  
  422.     *rMin = *rMax = [self valueOfLocation:0];
  423.     longSize = [self numLocations];
  424.  
  425.     /* first figure out what the maxs and mins are */
  426.     for ( index = 0 ; index < longSize; index++){
  427.         thisVal = [self valueOfLocation:index];
  428.         if (*rMin > thisVal)        *rMin = thisVal;
  429.         if (*rMax < thisVal)        *rMax = thisVal;
  430.         sumAll += thisVal;
  431.     }
  432.     *rMean = sumAll / longSize;
  433.     
  434.     [minField setFloatValue:*rMin];
  435.     [maxField setFloatValue:*rMax];
  436.     [meanField setFloatValue:*rMean];
  437.     return self;
  438. }
  439.  
  440. - renderVLines:(NXRect *)r  min:(float )minSheetVal max:(float )maxSheetVal
  441. {
  442.     int    longSize;    /* numRows or numCols, whichever is the long side */
  443.     int    index;
  444.     float    thisVal;
  445.     float    cellWidth;    /* the width of a unit (N) to plot */
  446.  
  447.     /* scale the plot */
  448.     PSscale(1.0, r->size.height / ( maxSheetVal - minSheetVal) );
  449.     PStranslate(0.0, -minSheetVal);
  450.         
  451.     longSize = [self numLocations];
  452.     cellWidth = r->size.width / (longSize);
  453.  
  454.     /* now plot each square */
  455.     PSsetgray(NX_BLACK);
  456.     thisVal = [self valueOfLocation:0];
  457.     PSmoveto(cellWidth/2.0, thisVal);
  458.     for ( index = 0 ; index < longSize; index++){
  459.         thisVal = [self valueOfLocation:index];
  460.         PSlineto(cellWidth*index+ cellWidth/2.0, thisVal);
  461.     }
  462.     PSstroke();
  463.     return self;
  464. }
  465.  
  466. /* we can position the bars anywhere inside the view by changing the
  467.  * insideRect parameter 
  468.  */
  469. - calcRect:(NXRect *)r 
  470.     ofBar:(int)n 
  471.     insideRect:(NXRect *)boundingRect
  472.     usingMin:(float)minSheetVal 
  473. {
  474.     float    thisVal;
  475.     float    cellWidth;    /* width of a unit (N) to plot */
  476.  
  477.     thisVal = [self valueOfLocation:n];
  478.     if (COM_Flags.drawType == DRAW_V_BAR){
  479.     cellWidth = boundingRect->size.width / (3 * [self numLocations] + 1);
  480.     r->origin.x = n * 3 * cellWidth + cellWidth;
  481.     if (thisVal < 0.0){
  482.                 r->origin.y = thisVal;
  483.                 r->size.height = -thisVal;
  484.     }else{
  485.                 r->origin.y = MAX(minSheetVal, 0.0);
  486.                 r->size.height= thisVal-r->origin.y;
  487.     }
  488.     r->size.width = 2 * cellWidth;
  489.     } else /* assume h bar */ {
  490.     cellWidth = boundingRect->size.height / (3 * [self numLocations] + 1);
  491.     r->origin.y = n * 3 * cellWidth + cellWidth;
  492.     if (thisVal < 0.0){
  493.                 r->origin.x = thisVal;
  494.                 r->size.width = -thisVal;
  495.     }else{
  496.                 r->origin.x = MAX(minSheetVal, 0.0);
  497.                 r->size.width= thisVal - r->origin.x;
  498.     }
  499.     r->size.height = 2 * cellWidth;
  500.     }
  501.  
  502.     return self;
  503. }
  504.  
  505. /* this should probably be empty but because Kris asked for a bar/lines switch 
  506.  * in the inspectorwe have the abillity to draw bars (vertical).  Of course  
  507.  * this makes the vertical bar drawing object almost codeless
  508.  */
  509. - renderBars:(NXRect *)r min:(float )minSheetVal max:(float )maxSheetVal
  510. {
  511.     int    longSize;    /* numRows/numCols, whichever is long side */
  512.     int    index;
  513.     NXRect    barRect;    /* bounding rectangle for bar in the chart */
  514.     NXColor    HSBColor;    /* in case randomBarColors */
  515.  
  516.     longSize = [self numLocations];
  517.     if (COM_Flags.drawType == DRAW_V_BAR){
  518.         /* scale the plot */
  519.         PSscale(1.0, r->size.height / ( maxSheetVal - minSheetVal) );
  520.         PStranslate(0.0, -minSheetVal);
  521.     } else /* assume h bar */ {
  522.         /* scale the plot */
  523.         PSscale(r->size.width / ( maxSheetVal - minSheetVal), 1.0 );
  524.         PStranslate( -minSheetVal, 0.0);
  525.     }
  526.         
  527.     /* now plot each square */
  528.     for ( index = 0 ; index < longSize; index++){
  529.         if (index == highlightIndex)
  530.             NXSetColor(highlightColor);
  531.         else if (!COM_Flags.randomBarColors) {
  532.             PSsetgray ((1.0 / (longSize+2.0)) * (index +1));
  533.         } else {
  534.             HSBColor = NXConvertHSBToColor(
  535.                     ((float)index / (float)longSize), 
  536.                 1.0, 1.0);
  537.             NXSetColor(HSBColor);
  538.         }
  539.         [self calcRect:&barRect ofBar:index
  540.             insideRect:(NXRect *)r
  541.             usingMin:minSheetVal];
  542.         NXRectFill(&barRect);
  543.     }
  544.     return self;
  545. }
  546.  
  547.  
  548. /* spacing:    Each plot is a width of 2N and each gap is a width of 1N
  549.  *        total width is m*(2N+N) + N  = 3mN+N = where m = number of bars
  550.  *        Thus N = width / (3m+1)
  551.  *
  552.  * scaling:    The scaling can make some stuff look pretty funny.  
  553.  */
  554. - drawSelf:(NXRect *)r :(int)c
  555. {    
  556.     float    minCellVal, maxCellVal;    /* min & max of values to be plotted */
  557.     float    meanCellVal;        /* mean of the plotted values */
  558.     float    minSheetVal,maxSheetVal;/* RangeOfNumbers will plot in graph */
  559.     NXRect    rectOfPlot;        /* bounds of rect that will hold plot */
  560.     NXSetColor(backgroundColor);
  561.     NXRectFill(&bounds);
  562.     PSsetgray(NX_BLACK);
  563.     switch    (borderType) {
  564.     case    NX_LINE:
  565.             NXFrameRect(&bounds);
  566.             break;
  567.     case    NX_BEZEL:
  568.             NXDrawWhiteBezel(&bounds,&bounds);
  569.             break;
  570.     case    NX_GROOVE:
  571.             NXDrawGroove(&bounds, &bounds);
  572.             break;
  573.     default:    break;
  574.     }
  575.     rectOfPlot= bounds;
  576.     rectOfPlot.origin.x += hMargin;
  577.     rectOfPlot.origin.y += vMargin;
  578.     rectOfPlot.size.width -=2*hMargin;
  579.     rectOfPlot.size.height -=2*vMargin;
  580.     if (        NX_WIDTH(&bounds) < (2*hMargin) || 
  581.             NX_HEIGHT(&bounds) < (2*vMargin) ) 
  582.         return self;
  583.         
  584.     
  585.     [window disableFlushWindow];
  586.     PSgsave();
  587.     PSsetlinewidth(0.0);
  588.     PStranslate (hMargin, vMargin);
  589.     
  590.     if (YES){
  591.     /* great we only have a single axis to work on */
  592.     [self calcMin:&minCellVal andMax:&maxCellVal andMean:&meanCellVal];
  593.  
  594.     if (COM_Flags.autoScale){
  595.         /* give us a plot if they are all the same (but not 0.0)  */
  596.         if (minCellVal == maxCellVal){
  597.                 if (minCellVal > 0.0)    minCellVal = 0.0;
  598.                 else if (maxCellVal < 0.0) maxCellVal = 0.0;
  599.         }
  600.         
  601.         /* figure out what min and the max should be on the sheet */
  602.         if (minCellVal == 0.0)    minSheetVal= 0.0;
  603.         else minSheetVal= minCellVal - ((maxCellVal - minCellVal)*0.2);
  604.         /* we shouldn't have pushed this across the origin */
  605.         if (minSheetVal < 0.0 && minCellVal > 0.0) minSheetVal = 0.0;
  606.         
  607.         if (maxCellVal == 0.0)    maxSheetVal = 0.0;
  608.         else maxSheetVal= maxCellVal +((maxCellVal - minCellVal)* 0.2);
  609.         /* make sure we didn't go across the origin */
  610.         if (maxSheetVal > 0.0 && maxCellVal < 0.0) maxSheetVal = 0.0;
  611.     } else {
  612.         minSheetVal = minSheetSet;
  613.         maxSheetVal = maxSheetSet;
  614.     }
  615.         
  616.     switch(COM_Flags.drawType){
  617.     case    DRAW_H_BAR:
  618.     case    DRAW_V_BAR:
  619.         [self renderBars:&rectOfPlot min:minSheetVal max:maxSheetVal];
  620.         break;
  621.     case    DRAW_H_LINE:
  622.         break;
  623.     case    DRAW_V_LINE:
  624.         [self renderVLines:&rectOfPlot min:minSheetVal max:maxSheetVal ];
  625.         break;
  626.     default:
  627.         break;
  628.     }
  629.  
  630.     PSgrestore();
  631.     if (COM_Flags.drawFrame){
  632.         PSsetgray(NX_BLACK);
  633.         NXFrameRect(&rectOfPlot);
  634.     }
  635.     }
  636.     [window reenableFlushWindow];
  637.     return self;
  638. }
  639.  
  640. @end
  641.